/*
 * *****************************************************************************
 * Copyright (C) 2014-2024 Dennis Sheirer
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 * ****************************************************************************
 */

package io.github.dsheirer.module.decode.p25.phase2.message.mac;

import io.github.dsheirer.module.decode.p25.reference.Vendor;
import java.util.EnumSet;
import java.util.Map;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * MAC opcode is used with MAC_IDLE, MAC_ACTIVE and MAC_HANGTIME PDU format messages
 *
 * Opcode Lengths used in the enumeration entries:
 * a) >0 = discrete length
 * b) Integer.MIN_VALUE = variable length
 * c) -1 = unknown length
 */
public enum MacOpcode
{
    PUSH_TO_TALK(Vendor.STANDARD, -1, "PUSH-TO-TALK", -1),
    END_PUSH_TO_TALK(Vendor.STANDARD, -1, "END_PUSH-TO-TALK", -1),

    TDMA_00_NULL_INFORMATION_MESSAGE(Vendor.STANDARD, 0, "NULL INFORMATION", -1),
    TDMA_01_GROUP_VOICE_CHANNEL_USER_ABBREVIATED(Vendor.STANDARD, 1, "GROUP VOICE CHANNEL USER ABBREVIATED", 7),
    TDMA_02_UNIT_TO_UNIT_VOICE_CHANNEL_USER_ABBREVIATED(Vendor.STANDARD, 2, "UNIT-TO-UNIT VOICE CHANNEL USER ABBREVIATED", 8),
    TDMA_03_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER(Vendor.STANDARD, 3, "TELEPHONE INTERCONNECT VOICE CHANNEL USER", 7),
    TDMA_05_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_IMPLICIT(Vendor.STANDARD, 5, "GROUP VOICE CHANNEL GRANT UPDATE MULTIPLE", 16),
    TDMA_08_NULL_AVOID_ZERO_BIAS(Vendor.STANDARD, 8, "NULL AVOID ZERO BIAS", Integer.MIN_VALUE),
    TDMA_10_MULTI_FRAGMENT_CONTINUATION_MESSAGE(Vendor.STANDARD, 16, "MULTI-FRAGMENT CONTINUATION MESSAGE", Integer.MIN_VALUE),
    TDMA_11_INDIRECT_GROUP_PAGING_WITHOUT_PRIORITY(Vendor.STANDARD, 17, "INDIRECT GROUP PAGING WITHOUT PRIORITY", Integer.MIN_VALUE),
    TDMA_12_INDIVIDUAL_PAGING_WITH_PRIORITY(Vendor.STANDARD, 18, "INDIVIDUAL PAGING WITH PRIORITY", Integer.MIN_VALUE),
    TDMA_21_GROUP_VOICE_CHANNEL_USER_EXTENDED(Vendor.STANDARD, 33, "GROUP VOICE CHANNEL USER EXTENDED", 14),
    TDMA_22_UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED(Vendor.STANDARD, 34, "UNIT-TO-UNIT VOICE CHANNEL USER EXTENDED", 15),
    TDMA_25_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_EXPLICIT(Vendor.STANDARD, 37, "GROUP VOICE CHANNEL GRANT UPDATE MULTIPLE EXPLICIT", 15),
    TDMA_30_POWER_CONTROL_SIGNAL_QUALITY(Vendor.STANDARD, 48, "POWER CONTROL SIGNAL QUALITY", 5),
    TDMA_31_MAC_RELEASE(Vendor.STANDARD, 49, "MAC RELEASE", 7),
    TDMA_PARTITION_0_UNKNOWN_OPCODE(Vendor.STANDARD, -1, "UNKNOWN TDMA OPCODE", -1),

    PHASE1_40_GROUP_VOICE_CHANNEL_GRANT_IMPLICIT(Vendor.STANDARD, 64, "GROUP VOICE CHANNEL GRANT IMPLICIT", 9),
    PHASE1_41_GROUP_VOICE_SERVICE_REQUEST(Vendor.STANDARD, 65, "GROUP VOICE SERVICE REQUEST", 7),
    PHASE1_42_GROUP_VOICE_CHANNEL_GRANT_UPDATE_IMPLICIT(Vendor.STANDARD, 66, "GROUP VOICE CHANNEL GRANT UPDATE IMPLICIT", 9),
    PHASE1_44_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_ABBREVIATED(Vendor.STANDARD, 68, "UNIT-TO-UNIT VOICE SERVICE CHANNEL GRANT ABBREVIATED", 9),
    PHASE1_45_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED(Vendor.STANDARD, 69, "UNIT-TO-UNIT ANSWER REQUEST ABBREVIATED", 10),
    PHASE1_46_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_ABBREVIATED(Vendor.STANDARD, 70, "UNIT-TO-UNIT VOICE CHANNEL GRANT UPDATE ABBREVIATED", 9),
    PHASE1_48_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_IMPLICIT(Vendor.STANDARD, 72, "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT IMPLICIT", 10),
    PHASE1_49_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE_IMPLICIT(Vendor.STANDARD, 73, "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT UPDATE IMPLICIT", 10),
    PHASE1_4A_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE(Vendor.STANDARD, 74, "TELEPHONE INTERCONNECT_ANSWER RESPONSE", 7),
    PHASE1_4C_RADIO_UNIT_MONITOR_COMMAND_ABBREVIATED(Vendor.STANDARD, 76, "RADIO UNIT MONITOR COMMAND ABBREVIATED", 10),
    PHASE1_52_SNDCP_DATA_CHANNEL_REQUEST(Vendor.STANDARD, 82, "SNDCP DATA CHANNEL REQUEST", 8),
    PHASE1_53_SNDCP_DATA_PAGE_RESPONSE(Vendor.STANDARD, 83, "SNDCP DATA PAGE RESPONSE", 9),
    PHASE1_54_SNDCP_DATA_CHANNEL_GRANT(Vendor.STANDARD, 84, "SNDCP DATA CHANNEL GRANT", 9),
    PHASE1_55_SNDCP_DATA_PAGE_REQUEST(Vendor.STANDARD, 85, "SNDCP DATA PAGE REQUEST", 7),
    PHASE1_58_STATUS_UPDATE_ABBREVIATED(Vendor.STANDARD, 88, "STATUS UPDATE ABBREVIATED", 10),
    PHASE1_5A_STATUS_QUERY_ABBREVIATED(Vendor.STANDARD, 90, "STATUS QUERY ABBREVIATED", 7),
    PHASE1_5C_MESSAGE_UPDATE_ABBREVIATED(Vendor.STANDARD, 92, "MESSAGE UPDATE ABBREVIATED", 10),
    PHASE1_5D_RADIO_UNIT_MONITOR_COMMAND_OBSOLETE(Vendor.STANDARD, 93, "RADIO UNIT MONITOR COMMAND **OBSOLETE**", 8),
    PHASE1_5E_RADIO_UNIT_MONITOR_ENHANCED_COMMAND_ABBREVIATED(Vendor.STANDARD, 94, "RADIO UNIT MONITOR ENHANCED COMMAND ABBREVIATED", 14),
    PHASE1_5F_CALL_ALERT_ABBREVIATED(Vendor.STANDARD, 95, "CALL ALERT ABBREVIATED", 7),
    PHASE1_60_ACKNOWLEDGE_RESPONSE_FNE_ABBREVIATED(Vendor.STANDARD, 96, "ACKNOWLEDGE RESPONSE FNE ABBREVIATED", 9),
    PHASE1_61_QUEUED_RESPONSE(Vendor.STANDARD, 97, "QUEUED RESPONSE", 9),
    PHASE1_64_EXTENDED_FUNCTION_COMMAND_ABBREVIATED(Vendor.STANDARD, 100, "EXTENDED FUNCTION COMMAND ABBREVIATED", 9),
    PHASE1_67_DENY_RESPONSE(Vendor.STANDARD, 103, "DENY RESPONSE", 9),
    PHASE1_68_GROUP_AFFILIATION_RESPONSE_ABBREVIATED(Vendor.STANDARD, 104, "GROUP AFFILIATION RESPONSE ABBREVIATED", 10),
    PHASE1_6A_GROUP_AFFILIATION_QUERY_ABBREVIATED(Vendor.STANDARD, 106, "GROUP AFFILIATION QUERY ABBREVIATED", 7),
    PHASE1_6B_LOCATION_REGISTRATION_RESPONSE(Vendor.STANDARD, 107, "LOCATION REGISTRATION RESPONSE", 10),
    PHASE1_6C_UNIT_REGISTRATION_RESPONSE_ABBREVIATED(Vendor.STANDARD, 108, "UNIT REGISTRATION RESPONSE ABBREVIATED",10),
    PHASE1_6D_UNIT_REGISTRATION_COMMAND_ABBREVIATED(Vendor.STANDARD, 109, "UNIT REGISTRATION COMMAND ABBREVIATED", 7),
    PHASE1_6F_DEREGISTRATION_ACKNOWLEDGE(Vendor.STANDARD, 111, "DEREGISTRATION ACKNOWLEDGE", 9),
    PHASE1_70_SYNCHRONIZATION_BROADCAST(Vendor.STANDARD, 112, "SYNCHRONIZATION BROADCAST", 9),
    PHASE1_71_AUTHENTICATION_DEMAND(Vendor.STANDARD, 113, "AUTHENTICATION DEMAND", 18), //Multi-Fragment
    PHASE1_72_AUTHENTICATION_FNE_RESPONSE_ABBREVIATED(Vendor.STANDARD, 114, "AUTHENTICATION FNE RESPONSE ABBREVIATED", 9),
    PHASE1_73_IDENTIFIER_UPDATE_TDMA_ABBREVIATED(Vendor.STANDARD, 115, "IDENTIFIER UPDATE TDMA ABBREVIATED", 9),
    PHASE1_74_IDENTIFIER_UPDATE_V_UHF(Vendor.STANDARD, 116, "IDENTIFIER UPDATE V/UHF", 9),
    PHASE1_75_TIME_AND_DATE_ANNOUNCEMENT(Vendor.STANDARD, 117, "TIME AND DATE ANNOUNCEMENT", 9),
    PHASE1_76_ROAMING_ADDRESS_COMMAND(Vendor.STANDARD, 118, "ROAMING ADDRESS COMMAND", 10),
    PHASE1_77_ROAMING_ADDRESS_UPDATE(Vendor.STANDARD, 119, "ROAMING ADDRESS UPDATE", 13),
    PHASE1_78_SYSTEM_SERVICE_BROADCAST(Vendor.STANDARD, 120, "SYSTEM SERVICE BROADCAST", 9),
    PHASE1_79_SECONDARY_CONTROL_CHANNEL_BROADCAST_IMPLICIT(Vendor.STANDARD, 121, "SECONDARY CONTROL CHANNEL BROADCAST IMPLICIT", 9),
    PHASE1_7A_RFSS_STATUS_BROADCAST_IMPLICIT(Vendor.STANDARD, 122, "RFSS STATUS BROADCAST IMPLICIT", 9),
    PHASE1_7B_NETWORK_STATUS_BROADCAST_IMPLICIT(Vendor.STANDARD, 123, "NETWORK STATUS BROADCAST IMPLICIT", 11),
    PHASE1_7C_ADJACENT_STATUS_BROADCAST_IMPLICIT(Vendor.STANDARD, 124, "ADJACENT STATUS BROADCAST IMPLICIT", 9),
    PHASE1_7D_IDENTIFIER_UPDATE(Vendor.STANDARD, 125, "IDENTIFIER UPDATE", 9),
    PHASE1_PARTITION_1_UNKNOWN_OPCODE(Vendor.STANDARD, -1, "UNKNOWN PHASE 1 OPCODE", -1),

    //Vendor partition opcodes using STANDARD vendor ID (observed on L3Harris systems)
    PHASE1_88_UNKNOWN_LCCH_OPCODE(Vendor.STANDARD, 136, "UNKNOWN OPCODE 136", 5),
    PHASE1_90_GROUP_REGROUP_VOICE_CHANNEL_USER_ABBREVIATED(Vendor.STANDARD, 144, "GROUP REGROUP VOICE CHANNEL USER ABBREVIATED", 7),

    L3HARRIS_81_UNKNOWN_OPCODE_129(Vendor.HARRIS, 129, "L3HARRIS UNKNOWN OPCODE 129", Integer.MIN_VALUE),
    L3HARRIS_8F_UNKNOWN_OPCODE_143(Vendor.HARRIS, 143, "L3HARRIS UNKNOWN OPCODE 143", Integer.MIN_VALUE),
    L3HARRIS_A0_PRIVATE_DATA_CHANNEL_GRANT(Vendor.HARRIS, 160, "L3HARRIS PRIVATE DATA CHANNEL GRANT", 9),
    L3HARRIS_A8_TALKER_ALIAS(Vendor.HARRIS, 168, "L3HARRIS TALKER ALIAS", -1),
    L3HARRIS_AA_GPS_LOCATION(Vendor.HARRIS, 170, "L3HARRIS GPS LOCATION", 17),
    L3HARRIS_AC_UNIT_TO_UNIT_DATA_CHANNEL_GRANT(Vendor.HARRIS, 172, "L3HARRIS UNIT-2-UNIT DATA CHANNEL GRANT", 12),
    L3HARRIS_B0_GROUP_REGROUP_EXPLICIT_ENCRYPTION_COMMAND(Vendor.HARRIS, 176, "L3HARRIS GROUP REGROUP EXPLICIT ENCRYPTION COMMAND", Integer.MIN_VALUE),

    MOTOROLA_80_GROUP_REGROUP_VOICE_CHANNEL_USER_ABBREVIATED(Vendor.MOTOROLA, 128, "MOTOROLA GROUP REGROUP VOICE CHANNEL USER ABBREVIATED", 8),
    MOTOROLA_81_GROUP_REGROUP_ADD(Vendor.MOTOROLA, 129, "MOTOROLA GROUP REGROUP ADD", 17),
    MOTOROLA_82_ACTIVE_GROUP_RADIOS_130(Vendor.MOTOROLA, 130, "MOTOROLA ACTIVE GROUP RADIOS 130", Integer.MIN_VALUE),
    MOTOROLA_83_GROUP_REGROUP_VOICE_CHANNEL_UPDATE(Vendor.MOTOROLA, 131, "MOTOROLA INDIVIDUAL REGROUP ACTIVE", 7),
    MOTOROLA_84_GROUP_REGROUP_EXTENDED_FUNCTION_COMMAND(Vendor.MOTOROLA, 133, "MOTOROLA GROUP REGROUP", 11),
    MOTOROLA_87_UNKNOWN_OPCODE_135(Vendor.MOTOROLA, 135, "MOTOROLA UNKNOWN OPCODE 135", Integer.MIN_VALUE),
    MOTOROLA_89_GROUP_REGROUP_DELETE(Vendor.MOTOROLA, 137, "MOTOROLA GROUP REGROUP DELETE", 17),
    MOTOROLA_8B_TDMA_DATA_CHANNEL(Vendor.MOTOROLA, 139, "MOTOROLA TDMA DATA CHANNEL", Integer.MIN_VALUE),
    MOTOROLA_8F_ACTIVE_GROUP_RADIOS_143(Vendor.MOTOROLA, 143, "MOTOROLA ACTIVE GROUP RADIOS 143", Integer.MIN_VALUE),
    MOTOROLA_BF_ACTIVE_GROUP_RADIOS_191(Vendor.MOTOROLA, 191, "MOTOROLA ACTIVE GROUP RADIOS 191", Integer.MIN_VALUE),
    //Opcode 144 uses STANDARD vendor ID for some reason.
    MOTOROLA_91_TALKER_ALIAS_HEADER(Vendor.MOTOROLA, 145, "MOTOROLA TALKER ALIAS HEADER", 17),
    MOTOROLA_95_TALKER_ALIAS_DATA_BLOCK(Vendor.MOTOROLA, 149, "MOTOROLA TALKER ALIAS DATA BLOCK", 17),
    MOTOROLA_A0_GROUP_REGROUP_VOICE_CHANNEL_USER_EXTENDED(Vendor.MOTOROLA, 160, "MOTOROLA GROUP REGROUP VOICE CHANNEL USER EXTENDED", 16),
    MOTOROLA_A3_GROUP_REGROUP_CHANNEL_GRANT_IMPLICIT(Vendor.MOTOROLA, 163, "MOTOROLA GROUP REGROUP CHANNEL GRANT IMPLICIT", 11),
    MOTOROLA_A4_GROUP_REGROUP_CHANNEL_GRANT_EXPLICIT(Vendor.MOTOROLA, 164, "MOTOROLA GROUP REGROUP CHANNEL GRANT EXPLICIT", 13),
    MOTOROLA_A5_GROUP_REGROUP_CHANNEL_GRANT_UPDATE(Vendor.MOTOROLA, 165, "MOTOROLA GROUP REGROUP CHANNEL GRANT UPDATE", 11),
    MOTOROLA_A6_QUEUED_RESPONSE(Vendor.MOTOROLA, 166, "MOTOROLA QUEUED RESPONSE", 11),
    MOTOROLA_A7_DENY_RESPONSE(Vendor.MOTOROLA, 167, "MOTOROLA QUEUED RESPONSE", 11),
    MOTOROLA_A8_ACKNOWLEDGE_RESPONSE(Vendor.MOTOROLA, 168, "MOTOROLA ACKNOWLEDGE RESPONSE", 10),

    VENDOR_PARTITION_2_UNKNOWN_OPCODE(Vendor.STANDARD, -1, "UNKNOWN VENDOR OPCODE", Integer.MIN_VALUE), //Variable length

    PHASE1_C0_GROUP_VOICE_CHANNEL_GRANT_EXPLICIT(Vendor.STANDARD, 192, "GROUP VOICE CHANNEL GRANT EXPLICIT", 11),
    PHASE1_C3_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT(Vendor.STANDARD, 195, "GROUP VOICE CHANNEL GRANT UPDATE EXPLICIT", 8),
    PHASE1_C4_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_EXTENDED_VCH(Vendor.STANDARD, 196, "UNIT-TO-UNIT VOICE SERVICE CHANNEL GRANT EXTENDED VCH", 15),
    PHASE1_C5_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED(Vendor.STANDARD, 197, "UNIT-TO-UNIT ANSWER REQUEST EXTENDED", 14),
    PHASE1_C6_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_EXTENDED_VCH(Vendor.STANDARD, 198, "UNIT-TO-UNIT VOICE CHANNEL GRANT UPDATE EXTENDED VCH", 15),
    PHASE1_C7_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_EXTENDED_LCCH(Vendor.STANDARD, 199, "UNIT-TO-UNIT VOICE CHANNEL GRANT UPDATE EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_C8_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_EXPLICIT(Vendor.STANDARD, 200, "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT EXPLICIT", 12),
    PHASE1_C9_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT(Vendor.STANDARD, 201, "TELEPHONE INTERCONNECT VOICE CHANNEL GRANT UPDATE EXPLICIT", 12),
    PHASE1_CB_CALL_ALERT_EXTENDED_LCCH(Vendor.STANDARD, 203, "CALL ALERT EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_CC_RADIO_UNIT_MONITOR_COMMAND_EXTENDED_VCH(Vendor.STANDARD, 204, "RADIO UNIT MONITOR COMMAND EXTENDED VCH", 14),
    PHASE1_CD_RADIO_UNIT_MONITOR_COMMAND_EXTENDED_LCCH(Vendor.STANDARD, 205, "RADIO UNIT MONITOR COMMAND EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_CE_MESSAGE_UPDATE_EXTENDED_LCCH(Vendor.STANDARD, 206, "MESSAGE UPDATE EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_CF_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_EXTENDED_LCCH(Vendor.STANDARD, 207, "UNIT-TO-UNIT VOICE SERVICE GRANT EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_D6_SNDCP_DATA_CHANNEL_ANNOUNCEMENT(Vendor.STANDARD, 214, "SNDCP DATA CHANNEL ANNOUNCEMENT", 9),
    PHASE1_D8_STATUS_UPDATE_EXTENDED_VCH(Vendor.STANDARD, 216, "STATUS UPDATE EXTENDED VCH", 14),
    PHASE1_D9_STATUS_UPDATE_EXTENDED_LCCH(Vendor.STANDARD, 217, "STATUS UPDATE EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_DA_STATUS_QUERY_EXTENDED_VCH(Vendor.STANDARD, 218, "STATUS QUERY EXTENDED VCH", 11),
    PHASE1_DB_STATUS_QUERY_EXTENDED_LCCH(Vendor.STANDARD, 219, "STATUS QUERY EXTENDED LCCH", 18), //Multi-Fragment
    PHASE1_DC_MESSAGE_UPDATE_EXTENDED_VCH(Vendor.STANDARD, 220, "MESSAGE UPDATE EXTENDED VCH", 14),
    PHASE1_DE_RADIO_UNIT_MONITOR_ENHANCED_COMMAND_EXTENDED(Vendor.STANDARD, 222, "RADIO UNIT MONITOR ENHANCED COMMAND EXTENDED", 18), //Multi-Fragment
    PHASE1_DF_CALL_ALERT_EXTENDED_VCH(Vendor.STANDARD, 223, "CALL ALERT EXTENDED VCH", 11),
    PHASE1_E0_ACKNOWLEDGE_RESPONSE_FNE_EXTENDED(Vendor.STANDARD, 224, "ACKNOWLEDGE RESPONSE FNE EXTENDED", 18), //Multi-Fragment
    PHASE1_E4_EXTENDED_FUNCTION_COMMAND_EXTENDED_VCH(Vendor.STANDARD, 228, "EXTENDED FUNCTION COMMAND EXTENDED VCH", 17),
    PHASE1_E5_EXTENDED_FUNCTION_COMMAND_EXTENDED_LCCH(Vendor.STANDARD, 229, "EXTENDED FUNCTION COMMAND EXTENDED LCCH", 14),
    PHASE1_E8_GROUP_AFFILIATION_RESPONSE_EXTENDED(Vendor.STANDARD, 232, "GROUP AFFILIATION RESPONSE EXTENDED", 16),
    PHASE1_E9_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT(Vendor.STANDARD, 233, "SECONDARY CONTROL CHANNEL BROADCAST EXPLICIT", 8),
    PHASE1_EA_GROUP_AFFILIATION_QUERY_EXTENDED(Vendor.STANDARD, 234, "GROUP AFFILIATION QUERY EXTENDED", 11),
    PHASE1_EC_UNIT_REGISTRATION_RESPONSE_EXTENDED(Vendor.STANDARD, 236, "UNIT REGISTRATION RESPONSE EXTENDED",13),
    PHASE1_F2_AUTHENTICATION_FNE_RESPONSE_EXTENDED(Vendor.STANDARD, 242, "AUTHENTICATION FNE RESPONSE EXTENDED", 16),
    PHASE1_F3_IDENTIFIER_UPDATE_TDMA_EXTENDED(Vendor.STANDARD, 243, "IDENTIFIER UPDATE TDMA EXTENDED", 14),
    PHASE1_FA_RFSS_STATUS_BROADCAST_EXPLICIT(Vendor.STANDARD, 250, "RFSS STATUS BROADCAST EXPLICIT", 11),
    PHASE1_FB_NETWORK_STATUS_BROADCAST_EXPLICIT(Vendor.STANDARD, 251, "NETWORK STATUS BROADCAST EXPLICIT", 13),
    PHASE1_FC_ADJACENT_STATUS_BROADCAST_EXPLICIT(Vendor.STANDARD, 252, "ADJACENT STATUS BROADCAST EXPLICIT", 11),
    PHASE1_FE_ADJACENT_STATUS_BROADCAST_EXTENDED_EXPLICIT(Vendor.STANDARD, 254, "ADJACENT STATUS BROADCAST EXTENDED EXPLICIT", 15),

    PHASE1_EXTENDED_PARTITION_3_UNKNOWN_OPCODE(Vendor.STANDARD, -1, "UNKNOWN EXTENDED PHASE 1 OPCODE", 1),

    UNKNOWN(Vendor.STANDARD, -1, "UNKNOWN", -1);

    private static final Map<Integer,MacOpcode> STANDARD_LOOKUP_MAP = new TreeMap<>();
    private static final Map<Integer,MacOpcode> HARRIS_LOOKUP_MAP = new TreeMap<>();
    private static final Map<Integer,MacOpcode> MOTOROLA_LOOKUP_MAP = new TreeMap<>();
    private static final Logger LOGGER = LoggerFactory.getLogger(MacOpcode.class);

    static
    {
        for(MacOpcode macOpcode : MacOpcode.values())
        {
            if(macOpcode.getValue() != -1)
            {
                switch(macOpcode.getVendor())
                {
                    case STANDARD -> STANDARD_LOOKUP_MAP.put(macOpcode.getValue(), macOpcode);
                    case HARRIS -> HARRIS_LOOKUP_MAP.put(macOpcode.getValue(), macOpcode);
                    case MOTOROLA -> MOTOROLA_LOOKUP_MAP.put(macOpcode.getValue(), macOpcode);
                    default -> LOGGER.warn("P25 Phase 2 Opcode [" + macOpcode.name() + "] - unrecognized vendor: " + macOpcode.getVendor());
                }
            }
        }
    }

    private Vendor mVendor;
    private int mValue;
    private String mLabel;
    private int mLength;

    /**
     * Constructor
     * @param vendor for the opcode
     * @param value of the opcode
     * @param label for display
     * @param length of the message in bytes.
     */
    MacOpcode(Vendor vendor, int value, String label, int length)
    {
        mVendor = vendor;
        mValue = value;
        mLabel = label;
        mLength = length;
    }

    /**
     * Multi-Fragment opcodes that use Opcode 0x10 Multi-Fragment Continuation message for subsequent message fragments.
     */
    public static EnumSet<MacOpcode> MULTI_FRAGMENT = EnumSet.of(PHASE1_71_AUTHENTICATION_DEMAND,
            PHASE1_C7_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_EXTENDED_LCCH,
            PHASE1_CB_CALL_ALERT_EXTENDED_LCCH, PHASE1_CD_RADIO_UNIT_MONITOR_COMMAND_EXTENDED_LCCH,
            PHASE1_CE_MESSAGE_UPDATE_EXTENDED_LCCH, PHASE1_CF_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_EXTENDED_LCCH,
            PHASE1_D9_STATUS_UPDATE_EXTENDED_LCCH, PHASE1_DB_STATUS_QUERY_EXTENDED_LCCH,
            PHASE1_DE_RADIO_UNIT_MONITOR_ENHANCED_COMMAND_EXTENDED, PHASE1_E0_ACKNOWLEDGE_RESPONSE_FNE_EXTENDED);

    /**
     * Call maintenance
     */
    public static EnumSet<MacOpcode> CALL_MAINTENANCE = EnumSet.of(PUSH_TO_TALK, END_PUSH_TO_TALK,
            PHASE1_48_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_IMPLICIT,
            PHASE1_49_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE_IMPLICIT,
            PHASE1_C8_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_EXPLICIT,
            TDMA_01_GROUP_VOICE_CHANNEL_USER_ABBREVIATED, TDMA_02_UNIT_TO_UNIT_VOICE_CHANNEL_USER_ABBREVIATED,
            TDMA_03_TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER, L3HARRIS_B0_GROUP_REGROUP_EXPLICIT_ENCRYPTION_COMMAND);

    /**
     * Data channel grants
     */
    public static EnumSet<MacOpcode> DATA_GRANTS = EnumSet.of(PHASE1_54_SNDCP_DATA_CHANNEL_GRANT);

    /**
     * Mobile requests and responses
     */
    public static EnumSet<MacOpcode> MOBILE_REQUEST_RESPONSE = EnumSet.of(PHASE1_41_GROUP_VOICE_SERVICE_REQUEST,
            PHASE1_58_STATUS_UPDATE_ABBREVIATED, PHASE1_5C_MESSAGE_UPDATE_ABBREVIATED,
            PHASE1_D8_STATUS_UPDATE_EXTENDED_VCH, PHASE1_DC_MESSAGE_UPDATE_EXTENDED_VCH);

    /**
     * Network requests and responses
     */
    public static EnumSet<MacOpcode> NETWORK_REQUEST_RESPONSE = EnumSet.of(TDMA_00_NULL_INFORMATION_MESSAGE,
            TDMA_11_INDIRECT_GROUP_PAGING_WITHOUT_PRIORITY, TDMA_12_INDIVIDUAL_PAGING_WITH_PRIORITY,
            TDMA_30_POWER_CONTROL_SIGNAL_QUALITY, TDMA_31_MAC_RELEASE, PHASE1_45_UNIT_TO_UNIT_ANSWER_REQUEST_ABBREVIATED,
            PHASE1_4A_TELEPHONE_INTERCONNECT_ANSWER_RESPONSE, PHASE1_4C_RADIO_UNIT_MONITOR_COMMAND_ABBREVIATED,
            PHASE1_52_SNDCP_DATA_CHANNEL_REQUEST, PHASE1_53_SNDCP_DATA_PAGE_RESPONSE, PHASE1_55_SNDCP_DATA_PAGE_REQUEST,
            PHASE1_5A_STATUS_QUERY_ABBREVIATED,
            PHASE1_5D_RADIO_UNIT_MONITOR_COMMAND_OBSOLETE, PHASE1_5E_RADIO_UNIT_MONITOR_ENHANCED_COMMAND_ABBREVIATED,
            PHASE1_5F_CALL_ALERT_ABBREVIATED, PHASE1_60_ACKNOWLEDGE_RESPONSE_FNE_ABBREVIATED, PHASE1_61_QUEUED_RESPONSE,
            PHASE1_68_GROUP_AFFILIATION_RESPONSE_ABBREVIATED,
            PHASE1_64_EXTENDED_FUNCTION_COMMAND_ABBREVIATED, PHASE1_67_DENY_RESPONSE,
            PHASE1_6A_GROUP_AFFILIATION_QUERY_ABBREVIATED, PHASE1_6B_LOCATION_REGISTRATION_RESPONSE,
            PHASE1_6C_UNIT_REGISTRATION_RESPONSE_ABBREVIATED, PHASE1_6D_UNIT_REGISTRATION_COMMAND_ABBREVIATED,
            PHASE1_6F_DEREGISTRATION_ACKNOWLEDGE, PHASE1_71_AUTHENTICATION_DEMAND,
            PHASE1_72_AUTHENTICATION_FNE_RESPONSE_ABBREVIATED, PHASE1_76_ROAMING_ADDRESS_COMMAND,
            PHASE1_C5_UNIT_TO_UNIT_ANSWER_REQUEST_EXTENDED,
            PHASE1_E8_GROUP_AFFILIATION_RESPONSE_EXTENDED, PHASE1_CC_RADIO_UNIT_MONITOR_COMMAND_EXTENDED_VCH,
            PHASE1_DA_STATUS_QUERY_EXTENDED_VCH, PHASE1_DF_CALL_ALERT_EXTENDED_VCH,
            PHASE1_E4_EXTENDED_FUNCTION_COMMAND_EXTENDED_VCH, PHASE1_EA_GROUP_AFFILIATION_QUERY_EXTENDED,
            PHASE1_F2_AUTHENTICATION_FNE_RESPONSE_EXTENDED);

    /**
     * Network and channel status and announcements
     */
    public static EnumSet<MacOpcode> NETWORK_STATUS = EnumSet.of(PHASE1_70_SYNCHRONIZATION_BROADCAST,
            PHASE1_73_IDENTIFIER_UPDATE_TDMA_ABBREVIATED,
            PHASE1_74_IDENTIFIER_UPDATE_V_UHF, PHASE1_75_TIME_AND_DATE_ANNOUNCEMENT,
            PHASE1_78_SYSTEM_SERVICE_BROADCAST, PHASE1_79_SECONDARY_CONTROL_CHANNEL_BROADCAST_IMPLICIT,
            PHASE1_7A_RFSS_STATUS_BROADCAST_IMPLICIT, PHASE1_7B_NETWORK_STATUS_BROADCAST_IMPLICIT,
            PHASE1_7C_ADJACENT_STATUS_BROADCAST_IMPLICIT, PHASE1_7D_IDENTIFIER_UPDATE,
            PHASE1_D6_SNDCP_DATA_CHANNEL_ANNOUNCEMENT, PHASE1_E9_SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT,
            PHASE1_FA_RFSS_STATUS_BROADCAST_EXPLICIT, PHASE1_FB_NETWORK_STATUS_BROADCAST_EXPLICIT,
            PHASE1_FC_ADJACENT_STATUS_BROADCAST_EXPLICIT);

    /**
     * Voice channel grants
     */
    public static EnumSet<MacOpcode> VOICE_GRANTS = EnumSet.of(TDMA_05_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_IMPLICIT,
            TDMA_21_GROUP_VOICE_CHANNEL_USER_EXTENDED, TDMA_22_UNIT_TO_UNIT_VOICE_CHANNEL_USER_EXTENDED,
            TDMA_25_GROUP_VOICE_CHANNEL_GRANT_UPDATE_MULTIPLE_EXPLICIT, PHASE1_40_GROUP_VOICE_CHANNEL_GRANT_IMPLICIT,
            PHASE1_42_GROUP_VOICE_CHANNEL_GRANT_UPDATE_IMPLICIT, PHASE1_44_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_ABBREVIATED,
            PHASE1_46_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_ABBREVIATED, PHASE1_C0_GROUP_VOICE_CHANNEL_GRANT_EXPLICIT,
            PHASE1_C3_GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT, PHASE1_C4_UNIT_TO_UNIT_VOICE_SERVICE_CHANNEL_GRANT_EXTENDED_VCH,
            PHASE1_C6_UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE_EXTENDED_VCH);


    /**
     * Indicates if this is a multi-fragment mac structure opcode, meaning that there is a base message that is followed
     * by one or more Opcode 0x10 MultiFragmentContinuationMessage mac structures.
     */
    public boolean isMultiFragment()
    {
        return MULTI_FRAGMENT.contains(this);
    }

    /**
     * Indicates if this opcode is an SNDCP data channel grant opcode
     */
    public boolean isDataChannelGrant()
    {
        return DATA_GRANTS.contains(this);
    }


    /**
     * Indicates if the enumeration element is contained in one of the enumset groupings above.
     * @return true if the element is grouped.
     */
    public boolean isGrouped()
    {
        return CALL_MAINTENANCE.contains(this) || DATA_GRANTS.contains(this) ||
                MOBILE_REQUEST_RESPONSE.contains(this) || NETWORK_REQUEST_RESPONSE.contains(this) ||
                NETWORK_STATUS.contains(this) || VOICE_GRANTS.contains(this);
    }

    @Override
    public String toString()
    {
        return mLabel;
    }

    /**
     * Vendor for the opcode.
     */
    public Vendor getVendor()
    {
        return mVendor;
    }

    public int getValue()
    {
        return mValue;
    }

    /**
     * Length of the message in octets/bytes for the opcode
     */
    public int getLength()
    {
        return mLength;
    }

    /**
     * Indicates if the opcode is for a variable-length message
     */
    public boolean isVariableLength()
    {
        return getLength() == Integer.MIN_VALUE;
    }

    /**
     * Indicates if this opcode has an unknown length.
     */
    public boolean isUnknownLength()
    {
        return !isVariableLength() && getLength() < 0;
    }

    /**
     * Indicates if this opcode falls in the vendor/custom opcode partition.
     */
    public boolean isVendorPartition()
    {
        return (128 <= getValue() && getValue() <= 191) || this.equals(VENDOR_PARTITION_2_UNKNOWN_OPCODE);
    }

    /**
     * Lookup the MAC opcode from an integer value
     */
    public static MacOpcode fromValue(int value)
    {
        MacOpcode mapValue = STANDARD_LOOKUP_MAP.get(value);
        if (mapValue != null)
        {
            return mapValue;
        }

        if(0 <= value && value <= 63)
        {
            return TDMA_PARTITION_0_UNKNOWN_OPCODE;
        }
        else if(64 <= value && value <= 127)
        {
            return PHASE1_PARTITION_1_UNKNOWN_OPCODE;
        }
        else if(128 <= value && value <= 191)
        {
            return VENDOR_PARTITION_2_UNKNOWN_OPCODE;
        }
        else if(192 <= value && value <= 255)
        {
            return PHASE1_EXTENDED_PARTITION_3_UNKNOWN_OPCODE;
        }

        return UNKNOWN;
    }

    /**
     * Lookup opcode from value and vendor.
     * @param value of the opcode
     * @param vendor specified in the mac message
     * @return vendor specific opcode or standard opcode equivalent if one is not found.
     */
    public static MacOpcode fromValue(int value, Vendor vendor)
    {
        switch(vendor)
        {
            case HARRIS:
                if(HARRIS_LOOKUP_MAP.containsKey(value))
                {
                    return HARRIS_LOOKUP_MAP.get(value);
                }
                else
                {
                    return fromValue(value);
                }
            case MOTOROLA:
                if(MOTOROLA_LOOKUP_MAP.containsKey(value))
                {
                    return MOTOROLA_LOOKUP_MAP.get(value);
                }
                else
                {
                    return fromValue(value);
                }
            case STANDARD:
            default:
                return fromValue(value);
        }
    }
}
